#ifndef YHAIDA_PROTOBUFCODECLITE_H
#define YHAIDA_PROTOBUFCODECLITE_H
#include "../../server/base/Utils.h"
#include "../../server/datetime/Timestamp.h"
#include "../../server/net/Callbacks.h"
#include "../../server/logging/Logging.h"
#include "../../server/net/TcpConnection.h"
#include "../../server/base/google-inl.h"
#include "../../server/net/SocketsOps.h"
#include <google/protobuf/message.h>
#include <string>
#include <memory.h>
#include <zlib.h>
namespace yhaida
{
    typedef std::shared_ptr<google::protobuf::Message> MessagePtr;

    // wire format
    //
    // Field     Length  Content
    //
    // size      4-byte  M+N+4
    // tag       M-byte  could be "RPC0", etc.
    // payload   N-byte
    // checksum  4-byte  adler32 of tag+payload
    class ProtobufCodecLite : Utils::nocapyable//ProtobufCodecLite是基类
    {
    public:
        const static int kHeaderLen = sizeof(int32_t);
        const static int kChecksumLen = sizeof(int32_t);
        const static int kMaxMessageLen = 64 * 1024 * 1024; // same as _codecstream.h kDefaultTotalBytesLimit

        enum ErrorCode //解码对应的状态码
        {
            kNoError = 0,
            kInvalidLength,
            kCheckSumError,
            kInvalidNameLen,
            kUnknownMessageType,
            kParseError,
        };

    public:
        typedef std::function<bool(const TcpConnectionPtr &,
                                   std::string,
                                   Timestamp)>
            RawMessageCallback;

        //后续分发的回调函数
        typedef std::function<void(const TcpConnectionPtr &,
                                   const MessagePtr &,
                                   Timestamp)>
            ProtobufMessageCallback;

        typedef std::function<void(const TcpConnectionPtr &,
                                   Buffer *,
                                   Timestamp,
                                   ErrorCode)>
            ErrorCallback;

    public:
        ProtobufCodecLite(const google::protobuf::Message *prototype,
                          std::string tagArg,
                          const ProtobufMessageCallback & messageCb,
                          const RawMessageCallback &rawCb = RawMessageCallback(),
                          const ErrorCallback &errorCb = defaultErrorCallback)
            : _prototype(prototype),
              _tag(std::move(tagArg)),
              _messageCallback(messageCb),
              _rawCb(rawCb),
              _errorCallback(errorCb),
              kMinMessageLen(_tag.size() + kChecksumLen)
        {
        }
        virtual ~ProtobufCodecLite() = default;

        /*
         * 检验和
         */
        static int32_t asInt32(const char *buff);
        static int32_t checksum(const void *buff, int len);
        static bool validateChecksum(const char *buff, int len);

        //**********************************编码********************************
        //序列化到Buffer，virtual可以变成多态的方式复用
        virtual int serializeToBuffer(Buffer *buf, const google::protobuf::Message &message);
        //编码
        void fillEmptyBuffer(yhaida::Buffer *buff, const google::protobuf::Message &message);
        //发送
        void send(const TcpConnectionPtr &conn,
                  const google::protobuf::Message &message);
        //**********************************解码********************************
        //从Buffer反序列化
        virtual bool parseFromBuffer(std::string buff, google::protobuf::Message *message);
        ErrorCode parse(const char *buff, int len_buff, ::google::protobuf::Message *message);
        void onMessage(const TcpConnectionPtr &conn,
                       Buffer *buff,
                       Timestamp receiveTime);


        const std::string &tag() const { return _tag; }
        //处理错误码
        static const std::string &errorCodeToString(ErrorCode errorCode);
        static void defaultErrorCallback(const TcpConnectionPtr &,
                                         Buffer *,
                                         Timestamp,
                                         ErrorCode);

    private:
        const google::protobuf::Message *_prototype; //已经DescriptorPool->MessageFactory
        const std::string _tag;
        ProtobufMessageCallback _messageCallback;
        RawMessageCallback _rawCb;
        ErrorCallback _errorCallback;

    public:
        const int kMinMessageLen;
    };

    //模板
    template <typename MSG, const char *TAG, typename CODEC = ProtobufCodecLite>
    class ProtobufCodecLiteT
    {
        
        /**
         * @brief
         * std::is_base_of<B, D>::value
         * 1. B 和 D 都是类类型
         * 2. B 是 D 的基类（包括直接基类和间接基类）或 B 和 D 类型相同
         * 
         */
        static_assert(std::is_base_of<ProtobufCodecLite, CODEC>::value, "CODEC should be derived from ProtobufCodecLite");

    public:
        typedef std::shared_ptr<MSG> ConcreteMessagePtr;

        typedef std::function<void(const TcpConnectionPtr &,
                                   const ConcreteMessagePtr &,
                                   Timestamp)>
            ProtobufMessageCallback;

        typedef ProtobufCodecLite::RawMessageCallback RawMessageCallback;
        typedef ProtobufCodecLite::ErrorCallback ErrorCallback;

    public:
        explicit ProtobufCodecLiteT(const ProtobufMessageCallback &messageCb,
                                    const RawMessageCallback &rawCb = RawMessageCallback(),
                                    const ErrorCallback &errorCb = ProtobufCodecLite::defaultErrorCallback)
            : _messageCallback(messageCb),
              _codec(&MSG::default_instance(), //相当于prototype->New()
                     TAG,
                     std::bind(&ProtobufCodecLiteT::onRpcMessage, this, _1, _2, _3),
                     rawCb,
                     errorCb)
        {
        }
        void send(const TcpConnectionPtr &conn, const MSG &message)
        {
            _codec.send(conn, message);
        }
        void onMessage(const TcpConnectionPtr &conn,
                       Buffer *buff,
                       Timestamp receiveTime)
        {
            _codec.onMessage(conn, buff, receiveTime);
        }

        /**
         * @brief   开放一个protobuf message down_pointer_cast的接口
         * 
         * @param   buff      yhaida::Buffer 
         * @param   message   protobuf message
         */
        void onRpcMessage(const TcpConnectionPtr &conn,
                          const MessagePtr &message,
                          Timestamp receiveTime)
        {
            _messageCallback(conn, yhaida::down_pointer_cast<MSG>(message), receiveTime);
        }
        
        /**
         * @brief   开放一个往Buffer对象里面序列化message的接口
         * 
         * @param   buff      yhaida::Buffer 
         * @param   message   protobuf message
         */
        void fillEmptyBuffer(Buffer *buff, const MSG &message)
        {
            _codec.fillEmptyBuffer(buff, message);
        }
        const std::string &tag() const { return _codec.tag(); }

    private:
        ProtobufMessageCallback _messageCallback;
        CODEC _codec;
    };

}

#endif